package me.yczhang.kit.bank;

import me.yczhang.util.ReflectionUtil;
import org.springframework.context.ApplicationContext;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.stream.Stream;

/**
 * 实现是spring framework的实现,只是套了一层注入
 * Created by YCZhang on 7/15/16.
 */
public class SpringBeanFactory {

	private ApplicationContext context;

	private SpringBeanFactory(ApplicationContext context) {
		this.context = context;
	}

	public static SpringBeanFactory create(ApplicationContext context) {
		return new SpringBeanFactory(context);
	}

	/**
	 * 根据BeanField注入
	 * @param klass
	 * @param object
	 * @param <T>
	 */
	public <T> void inject(@Nonnull Class<T> klass, @Nullable T object) {
		try {
			Field[] fields = Stream.of(klass.getDeclaredFields())
					.filter(field -> field.getAnnotation(BeanField.class) != null)
					.filter(field -> object != null || Modifier.isStatic(field.getModifiers()))
					.toArray(Field[]::new);

			for (Field field : fields) {
				BeanField beanField = field.getAnnotation(BeanField.class);
				String id = beanField.id();
				boolean isAllowNull = beanField.isAllowNull();
				if (!isAllowNull && !this.context.containsBean(id)) {
					throw new BeanBankException("lack bean " + id);
				}
				Object value = null;
				if (this.context.containsBean(id)) {
					value = this.context.getBean(id);
				}
				if (value != null) {
					ReflectionUtil.setFieldValue(object, field, value);
				}
			}
		}
		catch (BeanBankException e) {
			throw e;
		}
		catch (Exception e) {
			throw new BeanBankException(e);
		}
	}

	/**
	 * 注入一个心的BeanBank
	 * @return
	 */
	public BeanBank build() {
		try {
			BeanBank beanBank = new BeanBank();
			for (String name : this.context.getBeanDefinitionNames()) {
				beanBank.put(name, this.context.getBean(name));
			}
			return beanBank;
		}
		catch (BeanBankException e) {
			throw e;
		}
		catch (Exception e) {
			throw new BeanBankException(e);
		}
	}

	/**
	 * 注入ObjBank
	 */
	public void injectIntoObjBank() {
		try {
			for (String name : this.context.getBeanDefinitionNames()) {
				ObjBank.put(name, this.context.getBean(name));
			}
		}
		catch (BeanBankException e) {
			throw e;
		}
		catch (Exception e) {
			throw new BeanBankException(e);
		}
	}

	/**
	 * 获取一个Bean
	 * @param id
	 * @param klass
	 * @param <T>
	 * @return
	 */
	public <T> T getBean(String id, Class<T> klass) {
		return this.context.getBean(id, klass);
	}

	/**
	 * 获取一个Bean
	 * @param id
	 * @return
	 */
	public Object getBean(String id) {
		return this.context.getBean(id);
	}

}
